iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0
Modern Web

30天React練功坊-攻克常見實務/面試問題系列 第 10

30天React練功坊-攻克常見實務/面試問題 Day10: useEffect got called twice with empty dependency array

  • 分享至 

  • xImage
  •  
tags: ItIron2023 react

前言

我們昨天看了一個useEffect在沒有處理好dependency array時發生的問題,今天讓我們再看一個更簡單的useEffect相關問題吧! 不過先劇透一下,實際上今天的useEffect是完全無辜的,會特別提到它僅僅是因為幾乎所有相關的提問都是在使用useEffect發現這個問題的,馬上來看一下吧!

本日題目

首先一樣請你看一下今天的codesandbox,程式碼就區區幾行,應該是史上最簡短的範例了。

export default function App() {
  useEffect(() => {
    console.log("I should just call once");
  }, []);
  return (
    <div className="App">
      <h1>useEffect got called twice with empty dependency array</h1>
      <p>Open the console to check the log messages</p>
    </div>
  );
}

day10-demo-image

程式碼相當的單純,頁面中僅有一個useEffect在處理邏輯,其餘的部分就只是單純的渲染。我們之前有提過useEffect主要是由三個部分組成,其中一個部分便是dependency array,當dependency array為空陣列時,便僅僅會在初次渲染時執行,之後state或是prop不管怎麼變動造成re-render都不會再次執行useEffect內的callback。但現在我們卻在console中看到了useEffect內印出的log被印了兩次,請試著解釋這情形發生的原因並試著修復這樣的情況。

解答與基本解釋

這也是常常登上stackoverflow問題區的疑問,尤其在許多新專案建立時會有人注意到這樣的問題因而上網求助,但實際上這份程式碼並沒有任何的問題,在目前示範的專案中,useEffect內的callback呼叫兩次完全是預期的行為。先別急,我知道有人心裡在罵爹娘了,明明前面我才跟你說過當dependency array為空陣列時,裡面的callback便僅僅會在初次渲染時執行,且聽我娓娓道來。

一般來說當你的effect執行多次時會有很多種可能的原因,比方說你不小心讓父組件重新掛載了子組件之類的,但這次的範例非常單純,很明顯不是這類的問題,實際上問題是在盒子之外,也就是另一個檔案,請你切到專案中index.js的檔案。

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode> 
    <App />
  </StrictMode>
);

上方的StrictMode就是這次的兇手了,在StrictMode底下會讓你的hooks跑兩次,所以只要移除掉StrictMode的使用就沒事囉!大家可以回家啦!如果真的只寫這樣我倒不如一頭撞豆腐撞死算了,當然還是要做點基本解釋。

我們要先理解,為什麼react要讓你的hooks在StrictMode下執行兩次?
一般當我們撰寫組件時,我們會希望組件跟威士忌一樣,越純越好(pure),也就是說我們並不希望自己寫的組件造成一些意料之外的side effect,比方説之前提到的沒寫cleanup意外造成的memory leak就是一個常見的例子。但react並沒有辦法替你判斷哪些side effect是你所預期的而哪些又不是,因此它用了不同的做法,藉由讓你的hook多跑幾次,讓一些本來不是這麼明顯的side effect變得明顯一些,最終由你自己去抓出可能的意外,而這樣的行為僅在dev模式會套用,在正式環境並不會有這樣的行為,因此即便你不做任何處理也不會有問題,但若你真的覺得這樣的行為很煩人,那麼主動移除掉即可,StrictMode是否真的帶給開發者足夠的好處也是一直社群爭論的話題之一,我今天在這並不打算開這個戰場。

總結

我們今天看了一個其實與uesEffect並沒有直接相關的問題,不過這確實是經常綁在一起討論的東西,我們這幾天的文章看下來你應該有發現react有許多邏輯都是為了讓開發者不要寫出太buggy的程式碼而生的,你若是不理解根本的原因並照它原先的規則去寫,往往你會寫出一些anti-pattern的玩意。但說實在,我並不認為這完全是開發者的問題,react為了一些效能上的優化其實也犧牲了很多工程師的開發體驗,原本一段短短的程式碼為了避免不必要的重複渲染可能會用許多hook包裝,最終變成一大包東西,這也是react為人詬病的其中一個點,我們在之後的文章會有機會探討到一個精簡版的問題,也許能讓你了解我的意思,但今天就先到此為止吧!

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!


上一篇
30天React練功坊-攻克常見實務/面試問題 Day9: Data fetch with useEffect not work as expected
下一篇
30天React練功坊-攻克常見實務/面試問題 Day11: Race condition with useEffect
系列文
30天React練功坊-攻克常見實務/面試問題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言